/* 
 * Copyright 2009 HIT-CIR (HIT Center for Information Retrieval). 
 * 
 * author: chuter
 * mail:   lliu@ir.hit.edu.cn
 */
package edu.hit.irlab.coref.mentionfinder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.hit.irlab.coref.util.LinguisticCalc;

import nlpeap.feature.basictypes.GenderType;
import nlpeap.feature.basictypes.NumberType;
import nlpeap.knowledge.SemanticClass.SemanticClassEnum;
import nlpeap.tms.Document;
import nlpeap.tms.Sentence;
import nlpeap.tms.Token;


/**
 * The description of a <i>Mention</i> object.<br>
 * 
 * Including <i>extent</i>, <i>head</i>, reference information,
 * and the corresponding <i>Document</i> information and so on.<br>
 * In addition, the <i>MentionType</i> including <i>gender</i>, 
 * <i>number</i>, <i>semantic class</i>, etc. are  determined as well.
 * 
 * @author chuter
 * @since Corference Resolution Platform
 */
public class Mention implements Comparable<Mention> {
	public static Log _log = LogFactory.getLog(Mention.class.getName());
	public int mention_index;
	private int extentBeginTokenId = -1;
	private int extentEndTokenId = -1;
	private int headBeginTokenId = -1;
	private int headEndTokenId = -1;
	//the id of the sentence from which the mention extracted
	private int sentenceId = -1;
	//the right id of the entity that this mention belongs to
	private int set_id = -1;
	private int refMen_index = -1;
	
	private String extent;
	private String head;
	//extra type information of the mention, especially for particular domain 
	private String mentionSubType;
	// the probability this Mention belongs to a Entity
	private MentionType _mentionType;
	//the annotation information
	private MentionAnnotation _mentionAnnotType;
		
	private final Document doc;
	
	public Mention(int mention_index, Document doc) {
		this.mention_index = mention_index;
		this.doc = doc;
	}
	
	public void setMentionType(MentionType mentionType) {
		this._mentionType = mentionType;
	}
	
	public MentionType getMentionType(){
		if (_mentionType == null)
			_mentionType = LinguisticCalc.getInstance().calcMentionType(this);
		return _mentionType;
	}
	
	//set and get the MentionAnnotation
	public MentionAnnotation getMentionAnnotType()
	{ return this._mentionAnnotType; }
	public void setMentionAnnotType(MentionAnnotation mentionAnnotType)
	{ this._mentionAnnotType = mentionAnnotType; }
	
	public int getMentionIndex() { return mention_index; }
	
	/** get the start position in the <code>doc</code> 
	 * of the <code>Mention</code> */
	public int getLeftmostDiscoursePosition() {
		if (extentBeginTokenId == -1) return -1;
		return doc.gettoken(extentBeginTokenId).getstartdocumentpos();
	}
	/** get the end position in the <code>doc</code> 
	 * of the <code>Mention</code> */
	public int getRightmostDiscoursePosition() {
		if (extentEndTokenId == -1) return -1;
		return doc.gettoken(extentEndTokenId).getenddocumentpos();
	}
	
	public String getMentionSubType() {
		if (mentionSubType == null) {
			if (_mentionAnnotType != null)
				mentionSubType = _mentionAnnotType.getAnnotMentionSubType(); 
		}
		
		return mentionSubType;
	}
	
	public void setMentionSubType(String mentionSubType) {
		this.mentionSubType = mentionSubType;
	}
	
	public void setRefMention_index(int index) { refMen_index = index; }
	
	public int getRefMention_index() { return refMen_index; }
	
	/** get the id of the firt word of the extent */
	public int getExtentBegintokenId() { return extentBeginTokenId; }
	
	/** set the id of the first word of the extent */
	public void setExtentBegintokenId(int extentBeginTokenId) {
		if (extentBeginTokenId < 0) {
			_log.error("the id must greater than 0");
			return;
		}
		this.extentBeginTokenId = extentBeginTokenId;
	}
	
	/** get the id of the last word of the extent */
	public int getExtentEndtokenId() { return extentEndTokenId; }
	
	/** set the id of the last word of the extent */
	public void setExtentEndtokenId(int extentEndTokenId) {
		if (extentEndTokenId >= doc.getTokens().size()) {
			_log.error("the id is too large!!");
			return;
		}
		this.extentEndTokenId = extentEndTokenId;
	}
	
	/** get the id of the first word of the head */
	public int getHeadBegintokenId() { return headBeginTokenId;	}
	
	/** set the id of the first word of the head */
	public void setHeadBegintokenId(int headBeginTokenId) {
		if (headBeginTokenId < 0) {
			_log.error("the is must greater than 0");
			return;
		}
		this.headBeginTokenId = headBeginTokenId;
	}
	
	/** get the id of the last word of the head */
	public int getHeadEndtokenId() { return headEndTokenId;	}
	
	/** set the id of the last word of the head */
	public void setHeadEndtokenId(int headEndTokenId) {
		if (headEndTokenId >= doc.getTokens().size()) {
			_log.error("the id is too large!!");
			return;
		}
		this.headEndTokenId = headEndTokenId;
	}
	
	/** get the id of the sentence containing the mention */
	public int getSentenceId() { 
		if (sentenceId == -1)
			if (extentBeginTokenId>=0 && extentBeginTokenId<doc.getTokenNum())
				return doc.gettoken(extentBeginTokenId).getSentenceId();
		return sentenceId; 
	}
	
	/** set the id of the sentence containing the mention */
	public void setSentenceId(int sentenceId) {
		if (sentenceId >= doc.getSentences().size()) {
			_log.error("the id is too large!!");
			return;
		}
		
		this.sentenceId = sentenceId; 
	}
	
	public void setSetId( int set_id) { this.set_id = set_id; }
	public int getSetId() { return set_id; }
		
	public void setExtent(String extentstr) { extent = extentstr; }
	public String getExtent() { 
		if (extent == null) {
			if (extentBeginTokenId>=0 && extentEndTokenId>=0) {
				extent = "";
				for (int id = extentBeginTokenId; id <= extentEndTokenId; id++)
					extent += doc.gettoken(id).getContent();
				extent = extent.replaceAll("\n|\r", "");
			}
		} 
		return extent; 
	}
	
	public void setHead(String headstr) { head = headstr; }
	public String getHead() {
		if (head == null) {
			if (headBeginTokenId>=0 && headEndTokenId>=0) {
				head = "";
				for (int id = headBeginTokenId; id <= headEndTokenId; id++)
					head += doc.gettoken(id).getContent();
				head = head.replaceAll("\r|\n", "");
			} 
		} 
		return head;
	}
	
	public int compareTo(Mention arg) {
		return (
				this.getLeftmostDiscoursePosition() > arg.getLeftmostDiscoursePosition()) ? 1 : 
				(
						this.getLeftmostDiscoursePosition()==arg.getLeftmostDiscoursePosition()
					&& 
						this.getRightmostDiscoursePosition()>arg.getRightmostDiscoursePosition()
				) ? 1 : 0;
	}
	
	public String toString() {
		return "[ Extent:"+this.getExtent()+" head:"+this.getHead()
			  +" startposition:"+this.getLeftmostDiscoursePosition()+
			  " subtype:"+this.mentionSubType+" ]";
	}
	
	/** get the sentence from which this <code>Mention</code> extracted */
	public String getSentenceStr() { 
		if (sentenceId == -1) {
			_log.error("Please set the sentenceId first");
			return null;
		}
		return doc.getsentence(sentenceId).getSentStrCont();
	}
	
	/** get the right context of the <code>Mention</code> in tokens */
	public Token[] getRightContextToken(int tokenNum) {
		if (extentEndTokenId == -1) {
			_log.error("Please set the extentEndTokenId first");
			return null;
		}
		
		int id = extentEndTokenId + 1;
		tokenNum = (doc.getTokens().size()-id < tokenNum) 
					? (doc.getTokens().size()-id) : tokenNum;
		
		if (tokenNum < 0) tokenNum = 0;
		Token[] tokens = new Token[tokenNum];
		for (int i = 0; i < tokenNum; i++, id++)
			tokens[i] = doc.gettoken(id);
		
		return tokens;
	}
	
	/** get the right context of the <code>Mention</code> in tokens */
	public Token[] getLeftContextToken(int tokenNum) {
		if (extentBeginTokenId == -1) {
			_log.error("Please set the extentBeginTokenId first");
			return null;
		}
		
		int id = extentBeginTokenId;
		tokenNum = (id < tokenNum) ? id : tokenNum;
		
		if (tokenNum < 0) tokenNum = 0;
		Token[] tokens = new Token[tokenNum];
		for (int j = 0, i = id-tokenNum; j < tokenNum; i++, j++)
			tokens[j] = doc.gettoken(i);
		
		return tokens;
	}
	
	/** get the right context of the <code>Mention</code> in sentence */
	public Sentence[] getRightContextSent(int sentNum) {
		if (sentenceId == -1) {
			_log.error("Please set the sentenceId first");
			return null;
		}
		
		int id = sentenceId + 1;
		sentNum = (doc.getSentences().size()-id < sentNum) 
					? (doc.getSentences().size()-id) : sentNum;
		
		if (sentNum < 0) sentNum = 0;
		Sentence[] sents = new Sentence[sentNum];
		for (int i = 0; i < sentNum; i++, id++)
			sents[i] = doc.getsentence(id);
		
		return sents;
	}
	
	/** get the right context of the <code>Mention</code> in sentence */
	public Sentence[] getLeftContextSent(int sentNum) {
		if (sentenceId == -1) {
			_log.error("Please set the sentenceId first");
			return null;
		}
		
		int id = sentenceId;
		sentNum = (id < sentNum) ? id : sentNum;
		
		if (sentNum < 0) sentNum = 0;
		Sentence[] sents = new Sentence[sentNum];
		for (int j = 0, i = id-sentNum; j < sentNum; i++, j++)
			sents[j] = doc.getsentence(i);
		
		return sents;
	}
	
    /**Determine whether mention is coreferent with a given mention
     */
    public boolean isCoreferent(Mention m) {
        if (set_id == -1)
            return false;
        else if (set_id == m.getSetId())
            return true;
        else
            return false;
    }
    
    /** see if two mentions have overlapping spans */
    public boolean overlapsWith(Mention m) {
        if (getRightmostDiscoursePosition() <=
                m.getLeftmostDiscoursePosition())
            return false;
        else if (m.getRightmostDiscoursePosition() <=
                getLeftmostDiscoursePosition())
            return false;
        else
            return true;
    }
	
    /** see if two mentions have overlapping spans -
     *  like in [1 [2 his] treasure]
     */
    public boolean embeds(Mention m) {
        return
                getLeftmostDiscoursePosition() <= m.getLeftmostDiscoursePosition()
                &&
                getRightmostDiscoursePosition() >= m.getRightmostDiscoursePosition();
    }
    
    public int extentTokenSize()
    {
    	if (extentBeginTokenId==-1 || extentEndTokenId==-1)
    		return 0;
    	return extentEndTokenId-extentBeginTokenId+1;
    }
    
    public int headTokenSize()
    {
    	if (headEndTokenId==-1 || headBeginTokenId==-1)
    		return 0;
    	return headEndTokenId-headBeginTokenId+1;
    }
    
    /** get the tokens contained by the <i>Extent</i> of the mention */
    public Token[] getExtent_Tokens()
    {
    	if (extentTokenSize() <= 0) {
    		_log.debug(String.format("the extent info. hasn't been set"+
    				"for this mention: %s", this.toString()));
    		return new Token[]{};
    	}
    		
    	Token[] extent_token_arr = new Token[extentTokenSize()];
    	int i = 0;
    	for(int token_id = extentBeginTokenId; token_id <= extentEndTokenId; token_id++)
    		extent_token_arr[i++] = doc.gettoken(token_id);
    	
    	return extent_token_arr;
    }
    
    /** get the tokens contained by the <i>Head</i> of the mention */
    public Token[] getHead_Tokens()
    {
    	if (headTokenSize() <= 0) {
    		_log.debug(String.format("the head info. hasn't been set"+
    				"for this mention: %s", this.toString()));
    		return new Token[]{};
    	}
    		
    	Token[] head_token_arr = new Token[headTokenSize()];
    	int i = 0;
    	for(int token_id = headBeginTokenId; token_id <= headEndTokenId; token_id++)
    		head_token_arr[i++] = doc.gettoken(token_id);
    	
    	return head_token_arr;
    }
    
    public GenderType getGender() 
    { 
    	if (_mentionType == null) {
    		_log.debug(String.format("the mentionType hasn't been set "+
    				"for this mention: %s", this.toString()));
    		return GenderType.UNKNOWN;
    	}
    	
    	return _mentionType.gender;    	
    }
    
    public NumberType getNumber()
    {
    	if (_mentionType == null) {
    		_log.debug(String.format("the mentionType hasn't been set "+
    				"for this mention: %s", this.toString()));
    		return NumberType.UNKNOWN;
    	}
    	
    	return _mentionType.number;
    }
    
    /** returns true if mention is a Proper Name */
    public boolean getProperName() {
        return _mentionType.features.contains(MentionType.Features.isProperName);
    }
    
    /** returns true if mention is a pronoun */
    public boolean getPronoun(){
        return _mentionType.features.contains(MentionType.Features.isPronoun);
    }
    
    /** returns true if mention is Demonstrative */
    public boolean getDemonstrative(){
        return _mentionType.features.contains(MentionType.Features.isDemonstrative);
    }
    
    /** returns true if mention is Person Pronoun */
    public boolean getPerProNoun(){
        return _mentionType.features.contains(MentionType.Features.isPersPronoun);
    }
    /** returns true if mention is Demonstrative Pronoun */
    public boolean getDemNoun(){
        return _mentionType.features.contains(MentionType.Features.isDemPronoun);
    }
    /** returns true if mention is Demonstrative Nominal */
    public boolean getDemNominal(){
        return _mentionType.features.contains(MentionType.Features.isDemNominal);
    }
    /** returns true if mention is First or Second Person Pronoun */
    public boolean getIsFirstSecondPerson(){
        return _mentionType.features.contains(MentionType.Features.isFirstSecondPerson);
    }
    
    /** returns true if mention is a person name */
    public boolean getPersonName(){
        return _mentionType.features.contains(MentionType.Features.isPersonName);
    }
    /** returns true if mention is First or Second Person Pronoun */
    public SemanticClassEnum getSemanticClass(){
        return _mentionType.semanticClass;
    }
    
    /** get the sentence which contain the mention */
    public String getSentStr()
    { return doc.getsentence(getSentenceId()).getSentStrCont(); }
    
//    public int hashCode() {
//    	return String.format("%s%d%d", getHead(), getHeadBegintokenId(),
//    			getHeadEndtokenId()).hashCode();
//    }
    
    public Document getdoc() { return doc; }
    
    /** get the start position in the <i>Document</i> of the <i>Mention</i> */
    public int startPositionInDoc() {
    	Token token = doc.gettoken(extentBeginTokenId);
    	if (token == null) {
    		_log.error("the extent id hasn't been set for this Mention");
    		return -1;
    	} else {
    		return token.getstartdocumentpos();
    	}
    }
    
    /** get the end position in the <i>Document</i> of the <i>Mention</i> */
    public int endPositionInDoc() {
    	Token token = doc.gettoken(extentEndTokenId);
    	if (token == null) {
    		_log.error("the extent id hasn't been set for this Mention");
    		return -1;
    	} else {
    		return token.getenddocumentpos();
    	}
    }
    
    @Override
    public int hashCode() { return mention_index; }
    
    @Override
    public boolean equals(Object _otherMention)
    { return this.mention_index == ((Mention)_otherMention).mention_index; }
    
}
